#ifndef CHEETAH_MODEL_TRIANGLEMESH_H
#define CHEETAH_MODEL_TRIANGLEMESH_H

#include "cheetah/core.h"
#include "cheetah/geom/aabb.h"
#include "cheetah/graphics/material.h"

#include <vector>

namespace cheetah {
namespace model {
using namespace geom;
using graphics::Material;
class TriangleMesh {
 public:  
  static const int normal_size = sizeof(Normal);
  static const int vertex_size = sizeof(Point);
  static const int tex_coord_size = 2 * sizeof(float);
  static const int idx_size = 3 * sizeof(int); // deprecated as it's misleading
  static const int tri_idx_size = 3 * sizeof(int);

  TriangleMesh(Point *vertices, Normal *normals, float *tex_coords, 
               int *indices, int n_vertices, int n_triangles)
      : vertices_(vertices), normals_(normals), tex_coords_(tex_coords),
        indices_(indices), n_vertices_(n_vertices),
        n_triangles_(n_triangles), bound_need_update_(true) {
  }
  ~TriangleMesh();
  void Scale2Size(float size);
  void Scale(float scale);
  void Translate(const Vector3 &offset);
  void Translate2Pos(const Point &pos);
  void Transform(const Matrix4x4 &m);
  AABB Bound() const;

  int vertices_size() const { return this->n_vertices() * this->vertex_size; }
  int indices_size() const { return this->n_triangles() * this->tri_idx_size; }
  int n_triangles() const { return n_triangles_; }
  int n_vertices() const { return n_vertices_; }

  const Point *vertices() const { return vertices_; }
  const Normal *normals() const { return normals_; }
  const float *tex_coords() const { return tex_coords_; }
  const int *indices() const { return indices_; }
  
  const Point &vertex(int i) const { return vertices_[i]; }
  void set_vertex(int i, const Point &p) { vertices_[i] = p; }

  const Normal &normal(int i) const { return normals_[i]; }
  void set_normal(int i, const Normal &n) { normals_[i] = n; }
  const float *tex_coord(int i) const { return &tex_coords_[i]; }
  int index(int i) const { return indices_[i]; }

  bool has_tex() const { return NULL != tex_coords_; }
  const Material *material() const { return material_; }
  void set_material(const Material *mat) { material_ = mat; }
private:
  void ComputeBound() const;
private:
  const Material *material_;
  Point *vertices_;
  Normal *normals_;
  float *tex_coords_;
  int *indices_;
  int n_triangles_, n_vertices_;
  mutable AABB bound_;
  mutable bool bound_need_update_;
  //class Triangle should access the private data by accessors only
  friend class Triangle;
  DISABLE_COPY_AND_ASSIGN(TriangleMesh);
};

TriangleMesh *CreateSphere(const double radius, int slices, int stacks);
TriangleMesh *CreateCylinder(const double base_radius, 
                             const double top_radius, 
                             const double height,
                             int slices, int stacks);
TriangleMesh *CreateBoxMesh(const Point &pos1, const Point &pos2);
TriangleMesh *LoadObj(const char *filename);

void FinalizeModel(const Point *vertices, int n_vertices, int *indices,
                   int *face_count, Normal *normals);
//void FinalizeModel(const Point *vertices, int n_vertices, int *indices,
//                   int *face_count, Normal *normals);
//void FirstPass(FILE* fd, int *n_vertices, int *normal_count, 
//               int *texcoord_count, int *face_count);
//void ParseIndices(char *line, int *vertex_idx, int *texcoord_idx, 
//                  int *normal_idx);
typedef std::vector<const TriangleMesh*> MeshSet;
} // model
} // cheetah

#endif // CHEETAH_MODEL_TRIANGLEMESH_H
